﻿namespace DiffLCS

open System

type Action =
    | Deleted of string
    | Added of string
    | Untouched of string

type Diff =
    /// Validates a backtrack matrix for the strings "a" and "b"
    static member private validateMatrix (a : string) (b : string) (backtrack : int[,]) =
        // Assume not null
        if a.Length = backtrack.GetLength(0) - 1
            && b.Length = backtrack.GetLength(1) - 1 then
            ()
        else
            raise (new ArgumentException("The provided backtrack matrix´s dimensions does not match the provided strings", "backtrack"))
    
    /// Returns a diff list with the comparison between a new version "b" and an old version "a".
    static member Compare (a : string, b : string, backtrack : int[,]) =
        let C = match backtrack with
                | null -> LCS.MatrixSeq(a, b)   // Erstat med kommende LCS.Matrix(Auto)
                | C -> Diff.validateMatrix a b null;
                       C

        let untouched i j = i > 0 && j > 0 && a.[i - 1] = b.[j - 1]
        let added i j     = j > 0 && (i = 0 || C.[i, j - 1] >= C.[i - 1, j])
        let deleted i j   = i > 0 && (j = 0 || C.[i, j - 1] < C.[i - 1, j])

        let rec visitUntouched i0 i j =
            if untouched i j then
                visitUntouched i0 (i - 1) (j - 1)
            else
                Untouched(a.[i .. i0 - 1]) :: visit i j
        and visitAdded j0 i j =
            if not (untouched i j) && added i j then
                visitAdded j0 i (j - 1)
            else
                Added(b.[j .. j0 - 1]) :: visit i j
        and visitDeleted i0 i j =
            if not (untouched i j) && deleted i j then
                visitDeleted i0 (i - 1) j
            else
                Deleted(a.[i .. i0 - 1]) :: visit i j
        and visit i j =
            if untouched i j then
                visitUntouched i i j
            elif added i j then
                visitAdded j i j
            elif deleted i j then
                visitDeleted i i j
            else
                []

        visit a.Length b.Length |> List.rev

    /// Returns a diff list with the comparison between a new version "b" and an old version "a".
    static member Compare (a : string, b : string) =
        let backtrack = LCS.MatrixSeq(a, b)
        Diff.Compare(a, b, backtrack)